using System;
using System.Collections.Generic;
using System.Text;
using CodePlex.Resourceful.Amazon.S3;
using System.IO;

namespace CodePlex.Resourceful.CommandLine.Actions.S3
{
    public abstract class BaseS3 : ActionInfo
    {
        public BaseS3(string name, string description, params ParameterInfo[] parameters)
            : base(name, description, parameters)
        {


        }



      

        public override void Go(NameValuePairs parameters)
        {
            string awsKey = GetOptional<string>(parameters, "aws-key");
            string awsSecret = GetOptional<string>(parameters, "aws-secret");

            S3Connection connection = new S3Connection(awsKey, awsSecret);


            this.GoS3(connection, parameters);

        }

        protected void CheckFile(string file)
        {
            if (!File.Exists(file))
            {
                throw CodePlexException.Format("File [{0}] does not exist.", file);
            }
        }

        protected void CheckAcl(string acl)
        {
            if (!Utils.Exists(S3Constants.CannedAccessPolicies.All, acl))
            {
                throw CodePlexException.Format("Canned-acl must be one of {0}.", Utils.Join(",", S3Constants.CannedAccessPolicies.All));
            }
        }
        protected void CreateBucket(S3Connection connection, bool createBucket, string bucket, Datacenter location)
        {
            if (createBucket)
            {
                if (connection.IsBucketAvailable(bucket))
                {
                    Console.Out.WriteLine("Creating bucket [{0}] in location [{1}]...", bucket, location);
                    connection.CreateBucket(bucket,location);
                    Console.Out.WriteLine("Created bucket [{0}] in location [{1}]", bucket, location);
                }
            }
        }

        protected Datacenter CheckLocation(string location)
        {
            return (Datacenter)Enum.Parse(typeof(Datacenter), location,true);
        }


        abstract protected void GoS3(S3Connection connection, NameValuePairs parameters);
        protected static string DefaultAwsKey()
        {
            try
            {
                string aws = Environment.GetEnvironmentVariable("AWS");
                return aws.Split(':')[0];
            }
            catch { }
            return null;
        }


        protected static string DefaultAwsSecret()
        {
            try
            {
                string aws = Environment.GetEnvironmentVariable("AWS");
                return aws.Split(':')[1];
            }
            catch { }
            return null;
        }



        protected ParameterInfo FindParameter(string name)
        {
            return Utils.Find(this.Parameters, delegate(ParameterInfo item)
            {
                return item.Name == name;
            });
        }





        private object TweakIfEnumString<T>(object value)
        {
            if (typeof(T).IsEnum)
            {
                return ToEnum<T>((string)value);
            }
            return value;
        }

        protected T GetOptional<T>(NameValuePairs nvp, string name)
        {
            string value = nvp.GetIfExists(name);

            if (value == null)
            {
                return (T)TweakIfEnumString<T>(this.FindParameter(name).DefaultValue);
            }

            try
            {
                return Utils.ChangeType<T>(TweakIfEnumString<T>(value));
            }
            catch (Exception)
            {

                if (typeof(T).IsEnum)
                {
                    throw CodePlexException.Format("Parameter [{0}] value [{1}] must be one of: {2}", name, value, FormatEnumValues<T>());
                }
                else
                {
                    throw CodePlexException.Format("Parameter [{0}] value [{1}] must be a {2}.", name, value, typeof(T).Name);
                }
            }

        }

        // "sizes-differ" -> OverwriteMode.SizesDiffer
        protected static object ToEnum<T>(string value)
        {
            return Enum.Parse(typeof(T), Utils.Join("" ,Utils.ConvertAll<string,string>(value.Split('-'),delegate(string lower) {
                return lower.Substring(0, 1).ToUpper() + lower.Substring(1);
            })));
        }

        // OverwriteMode.SizesDiffer -> "sizes-differ"
        protected static string FromEnum<T>(T value)
        {
            return Utils.Join("-", Utils.SplitPascalIntoWords(value.ToString()).Split(' ')).ToLower();
        }

        protected static string FormatEnumValues<T>()
        {
            return Utils.Join(",", Utils.ConvertAll<string, string>(Enum.GetNames(typeof(T)), delegate(string enumName)
            {
                return FromEnum(enumName);
            }));
        }



        protected void PutObject(S3Connection connection, OverwriteMode overwrite, string bucket, string key, string acl, string file)
        {
            if (overwrite != OverwriteMode.Always)
            {
                bool exists = connection.IsObjectPresent(bucket, key);
                if (exists)
                {
                    if (overwrite == OverwriteMode.Never)
                    {
                        Console.Out.WriteLine("SKIPPING [{2}]  S3 object already exists in bucket [{0}] key [{1}], set overwrite:always to automatically overwrite existing objects.", bucket, key, Path.GetFileName(file));
                        return;
                    }

                    if (overwrite == OverwriteMode.SizesDiffer)
                    {
                        long localFileLength = new FileInfo(file).Length;
                        long existingObjectLength = long.Parse(connection.GetObjectHeaders(bucket, key)[HttpHeader.ContentLength]);

                        if (localFileLength == existingObjectLength)
                        {
                            Console.Out.WriteLine("SKIPPING [{2}]  S3 object already exists in bucket [{0}] key [{1}] with length [{3}], set overwrite:always to automatically overwrite existing objects.", 
                                bucket, 
                                key, 
                                Path.GetFileName(file),
                                Utils.ComputeSizeString( localFileLength));
                            return;
                        }
                    }

                }
            }

            Console.Out.WriteLine("Putting file [{0}] into bucket [{1}] key [{2}] with acl [{3}]...", file, bucket, key, acl);
            NameValuePairs headers = new NameValuePairs();
            headers[S3Constants.Headers.CannedAccessPolicy] = acl;
            long bytesSent = 0;
            Action<long> progress = delegate(long item)
            {
                bytesSent = item;
            };
            try
            {
                connection.PutObjectAsFile(bucket, key, headers, progress, file);

                Console.Out.WriteLine("Put-object complete.  Sent {0}", Utils.ComputeSizeString(bytesSent));

            }
            catch (S3Exception ex)
            {
                if (ex.Message.Contains("bucket does not exist"))
                {
                    throw CodePlexException.Format("Bucket [{0}] does not exist, set create-bucket:true to automatically create buckets.", bucket);
                }
                throw;
            }
        }
     




    }
}
